{smcl}
{* 10dec2021}{...}
{cmd:help mata mm_ebalance()}
{hline}

{title:Title}

{pstd}
    {bf:mm_ebalance() -- Entropy balancing}


{title:Syntax}

{pstd}
Initialize a new {cmd:mm_ebalance()} object

        {cmd:class mm_ebalance scalar} {it:S}
        or
        {it:S} = {cmd:mm_ebalance()}

{pstd}
Settings

{p2colset 9 30 32 2}{...}
{p2col:{it:S}{cmd:.tau(}{it:tau}{cmd:)}}target sum of balancing weights; {it:tau}
    can be a (strictly positive) real number or, alternatively, {cmd:"Wref"}
    (sum of weights in reference sample), {cmd:"W"} (sum of weight in main
    sample), {cmd:"Nref"} (number of rows in {it:Xref}), or {cmd:"N"} (number of
    rows in {it:X}); default is {cmd:"Wref"}
    {p_end}
{p2col:{it:S}{cmd:.scale(}{it:scale}{cmd:)}}scales to be used for standardization; 
    {it:scale} can be a (strictly positive) real vector providing custom values 
    (same length as columns in {it:X}) or, alternatively, 
    {cmd:"main"} (standard deviations from main sample), {cmd:"ref"}
    (standard deviations from reference sample), {cmd:"avg"} 
    (average between {cmd:main} and {cmd:ref}), {cmd:"wavg"} (weighted average 
    between {cmd:main} and {cmd:ref}), or {cmd:pooled} (standard deviations from
    pooled sample); standard deviations are computed using population formulas (division
    by N rather than N-1); scales equal to 0 will be reset to 1; default is {cmd:"main"}
    {p_end}
{p2col:{it:S}{cmd:.}[{cmd:no}]{cmd:adj(}{it:p}{cmd:)}}selects the means to be
    adjusted where {it:p} is a vector of indices referring to columns of {it:X};
    {it:S}{cmd:.adj(}{it:p}{cmd:)} causes all selected means to be adjusted and
    all non-selected means to be held fixed; alternatively, 
    {it:S}{cmd:.noadj(}{it:p}{cmd:)} causes all selected means to be held fixed
    and all non-selected means to be adjusted; the elements in {it:p} must be
    >= 1; duplicates and elements pointing to columns that do not exist
    will be ignored; specify {it:S}{cmd:.adj(.)} to adjust all means (the default)
    {p_end}
{p2col:{it:S}{cmd:.btol(}{it:btol}{cmd:)}}balancing tolerance; default is {bf:1e-6};
    balancing is achieved if the balancing loss is smaller than {it:btol}
    {p_end}
{p2col:{it:S}{cmd:.ltype(}{it:ltype}{cmd:)}}type of balancing loss function, can be {cmd:"reldif"}
    (maximum relative difference; the default), {cmd:"absdif"} 
    (maximum absolute difference), or {cmd:"norm"} (Euclidean norm of differences)
    {p_end}
{p2col:{it:S}{cmd:.etype(}{it:etype}{cmd:)}}evaluator type, can be {cmd:"bl"}
    (evaluator based on balancing loss; the default), {cmd:"wl"} 
    (evaluator based on distribution of weights), {cmd:"mm"} (method of moments
    evaluator), or {cmd:"mma"} (method of moments evaluator including intercept);
    when using an evaluator other than {cmd:"bl"} you might want to reduce 
    {it:vtol} a bit (e.g. to {bf:1e-10})
    {p_end}
{p2col:{it:S}{cmd:.trace(}{it:trace}{cmd:)}}trace level; default is {cmd:"value"};
    specify {cmd:"none"} to suppress iteration log; see
    {helpb mf_optimize##i_tracelevel:optimize()} for details
    {p_end}
{p2col:{it:S}{cmd:.maxiter(}{it:maxiter}{cmd:)}}maximum number of iterations;
    default is as set by {helpb set maxiter}
    {p_end}
{p2col:{it:S}{cmd:.ptol(}{it:ptol}{cmd:)}}convergence tolerance for coefficient
    vector; default is {bf:1e-6}; convergence is reached if {it:ptol} or {it:vtol} is
    satisfied; also see {helpb mf_optimize##i_ptol:optimize()}
    {p_end}
{p2col:{it:S}{cmd:.vtol(}{it:vtol}{cmd:)}}convergence tolerance for the minimization
    criterion; default is {bf:1e-7}; convergence is reached if {it:ptol} or {it:vtol} is
    satisfied; also see {helpb mf_optimize##i_ptol:optimize()}
    {p_end}
{p2col:{it:S}{cmd:.difficult(}{it:diff}{cmd:)}}stepping
    algorithm to be used in nonconcave regions; {it:diff}=0 (the default) uses
    the standard algorithm; {it:diff}!=0 uses an alternative algorithm; see the
    singular H methods in {helpb mf_optimize##i_singularH:optimize()} and
    the description of the {cmd:difficult} option in {helpb maximize}
    {p_end}
{p2col:{it:S}{cmd:.nostd(}{it:nostd}{cmd:)}}whether standardization should be applied
    during optimization to improve robustness of the algorithm; {it:nostd}=0 (the default)
    fits the coefficients using standardized data and then back-transforms the
    results; {it:nostd}=1 omits such standardization (not recommended); in any case,
    the balancing loss of the final fit will be obtained from the original data 
    {p_end}
{p2col:{it:S}{cmd:.nowarn(}{it:nowarn}{cmd:)}}whether "convergence not achieved"
    and "balance not achieved" messages should be displayed; {it:nowarn}=0 (the default)
    displays the messages; {it:nowarn}!=0 suppresses the messages{p_end}

{p 8 8 2}
Settings can be changed at any time, but changing a setting will clear
any results that may already exist in {it:S} (but not the data).

{p 8 8 2}
All functions for settings can be used without argument to retrieve a setting. For
example, type {it:ltype} = {it:S}{cmd:.ltype()} to retrieve the setting for the
type of loss function. The functions do not clear results if used in this form.

{pstd}
Provide data

{p 8 20 2}
{it:S}{cmd:.data(}{it:X}{cmd:,} {it:w}{cmd:,} {it:Xref}{cmd:,} {it:wref} [, {it:fast}]{cmd:)}

{p 7 20 2}{bind:       }{it:X}:  {it:real matrix} containing the main data (the data to be
    reweighted); rows are observations, columns are variables
    {p_end}
{p 7 20 2}{bind:       }{it:w}:  {it:real colvector} containing base weights;
    use {it:w} = {cmd:1} for unweighted data; if not scalar, {it:w} must have
    the same number of rows as {it:X}
    {p_end}
{p 7 20 2}{bind:    }{it:Xref}:  {it:real matrix} containing the reference data;
    rows are observations, columns are variables; {it:Xref} must have the same
    number of columns as {it:X}; if {it:Xref} only has a single row, it is
    assumed to contain population averages
    {p_end}
{p 7 20 2}{bind:    }{it:wref}:  {it:real colvector} containing base weights
    for the reference data; use {it:wref} = {cmd:1} for unweighted data; if not
    scalar, {it:wref} must have the same number of rows as {it:Xref}; if
    {it:Xref} only has a single row, {it:wref} is assumed to contain the
    population size
    {p_end}
{p 7 20 2}{bind:    }{it:fast}:  {it:real scalar} indicating whether checks for
    missing values and negative base weights should be skipped (to save computer
    time); specify {it:fast}!=0 to skip the checks; omitted {it:fast} is
    equivalent to {it:fast}=0; specify {it:fast}!=0 only if you are certain that
    there are no missing values and that there are no negative base weights;
    {cmd:mm_ebalance()} may break or return invalid results if either condition is violated
    {p_end}

{p 8 8 2}
Specifying {it:S}{cmd:.data()} clears any results that may already exist in {it:S}.

{pstd}
Retrieve results

{p2colset 9 34 36 2}{...}
{p2col:{it:b}{bind:      } = {it:S}{cmd:.b()}}fitted coefficients (column vector){p_end}
{p2col:{it:a}{bind:      } = {it:S}{cmd:.a()}}normalizing intercept (scalar){p_end}
{p2col:{it:wbal}{bind:   } = {it:S}{cmd:.wbal()}}balancing weights: {it:w} * exp({it:Xb} + {it:a}){p_end}
{p2col:{it:xb}{bind:     } = {it:S}{cmd:.xb()}}linear predictions: {it:Xb} + {it:a}{p_end}
{p2col:{it:pr}{bind:     } = {it:S}{cmd:.pr()}}propensity scores: invlogit({it:Xb} + {it:a} + ln({it:Wref}/{it:tau})){p_end}
{p2col:{it:madj}{bind:   } = {it:S}{cmd:.madj()}}means of {it:X} after reweighting{p_end}
{p2col:{it:wsum}{bind:   } = {it:S}{cmd:.wsum()}}total of balancing weights: sum({it:wbal}){p_end}
{p2col:{it:loss}{bind:   } = {it:S}{cmd:.loss()}}balancing loss at final fit{p_end}
{p2col:{it:bal}{bind:    } = {it:S}{cmd:.balanced()}}1 if balance is achieved ({it:loss} < {it:btol}), 0 else{p_end}
{p2col:{it:value}{bind:  } = {it:S}{cmd:.value()}}value of the optimization criterion at final fit{p_end}
{p2col:{it:iter}{bind:   } = {it:S}{cmd:.iter()}}number of iterations of the optimization algorithm{p_end}
{p2col:{it:conv}{bind:   } = {it:S}{cmd:.converged()}}1 if the optimization algorithm converged, 0 else{p_end}
{p2col:{it:IF_b}{bind:   } = {it:S}{cmd:.IF_b()}}influence functions of coefficients (main sample){p_end}
{p2col:{it:IFref_b}{bind:} = {it:S}{cmd:.IFref_b()}}influence functions of coefficients (reference sample){p_end}
{p2col:{it:IF_a}{bind:   } = {it:S}{cmd:.IF_a()}}influence function of intercept (main sample){p_end}
{p2col:{it:IFref_a}{bind:} = {it:S}{cmd:.IFref_a()}}influence function of intercept (reference sample){p_end}

{p 8 8 2}
Specifying any of the above functions will trigger estimation, if not already carried out.

{pstd}
Retrieve information on data

{p2colset 9 34 36 2}{...}
{p2col:{it:k}{bind:      } = {it:S}{cmd:.k()}}number of variables (columns) in {it:X} (number of coefficients){p_end}
{p2col:{it:omit}{bind:   } = {it:S}{cmd:.omit()}}column vector flagging collinear variables (omitted coefficients){p_end}
{p2col:{it:k_omit}{bind: } = {it:S}{cmd:.k_omit()}}number of collinear variables (omitted coefficients){p_end}
{p2col:{it:X}{bind:      } = {it:S}{cmd:.X()}}data of main sample{p_end}
{p2col:{it:w}{bind:      } = {it:S}{cmd:.w()}}base weights of main sample{p_end}
{p2col:{it:N}{bind:      } = {it:S}{cmd:.N()}}number of rows in main sample{p_end}
{p2col:{it:W}{bind:      } = {it:S}{cmd:.W()}}size (sum of weights) of main sample{p_end}
{p2col:{it:m}{bind:      } = {it:S}{cmd:.m()}}means of {it:X} (before reweighting){p_end}
{p2col:{it:s}{bind:      } = {it:S}{cmd:.s()}}standard deviations of {it:X}{p_end}
{p2col:{it:Xref}{bind:   } = {it:S}{cmd:.Xref()}}data of reference sample{p_end}
{p2col:{it:wref}{bind:   } = {it:S}{cmd:.wref()}}base weights of reference sample{p_end}
{p2col:{it:Nref}{bind:   } = {it:S}{cmd:.Nref()}}number of rows in reference sample{p_end}
{p2col:{it:Wref}{bind:   } = {it:S}{cmd:.Wref()}}size (sum of weights) of reference sample{p_end}
{p2col:{it:mref}{bind:   } = {it:S}{cmd:.mref()}}means of {it:Xref}{p_end}
{p2col:{it:sref}{bind:   } = {it:S}{cmd:.sref()}}standard deviations of {it:Xref}{p_end}
{p2col:{it:scale}{bind:  } = {it:S}{cmd:.scale()}}scales used for standardization{p_end}
{p2col:{it:mu}{bind:     } = {it:S}{cmd:.mu()}}target values for means of reweighted {it:X}{p_end}
{p2col:{it:tau}{bind:    } = {it:S}{cmd:.tau()}}target sum of balancing weight{p_end}


{title:Description}

{pstd}
    {cmd:mm_ebalance()} performs entropy balancing (Hainmueller 2012). The
    goal of {cmd:mm_ebalance()} is to find a vector of weights such that the
    means in the reweighted data of a given sample match the means in the data
    of a reference sample (i.e., to find weights that balance the means between
    the two samples) or, equivalently, to find weights that adjust the sample
    means to known population values.

{pstd}
    Estimation of the required coefficients is performed by calling Mata's
    {helpb mf_optimize:optimize()}. Collinear terms will be ignored during
    optimization. However, these terms will be taken into account when computing
    the final balancing loss after optimization has completed.

{pstd}
    In addition to the balancing weights and the coefficients,
    {cmd:mm_ebalance()} also provides influence functions that are useful for
    the computation of standard errors of the coefficients and for the correction
    of standard errors of statistics that have been estimated from reweighted
    data; see {browse "http://ideas.repec.org/p/bss/wpaper/39.html":Jann (2021b)},
    which also provides details on the methods and formulas implemented in
    {cmd:mm_ebalance()}.

{pstd}
    A wrapper for {cmd:mm_ebalance()} that obtains balancing weights in a single
    line of code is provided as {helpb mf_mm_wbal:mm_wbal()}. Furthermore, an
    older function for entropy balancing with somewhat different
    features is available as {helpb mf_mm_ebal:mm_ebal()}.

{pstd}
    See {helpb ebalfit} (Jann 2021a) for an easy to use Stata implementation
    of entropy balancing that is based on {cmd:mm_ebalance()}. For an alternative
    Stata implementation see {helpb ebalance} by Hainmueller and Xu (2011, 2013).


{title:Examples}

{dlgtab:Balance subsamples}

        . {stata sysuse nlsw88, clear}
        . {stata drop if missing(union, hours, ttl_exp, tenure)}
        . {stata generate byte nonunion = 1 - union}
        . {stata "mata:"}
        : {stata X    = st_data(., "hours ttl_exp tenure", "union")}
        : {stata Xref = st_data(., "hours ttl_exp tenure", "nonunion")}
        : {stata S = mm_ebalance()}
        : {stata S.data(X, 1, Xref, 1)}
        : {stata mean(Xref)', mean(X)', mean(X, S.wbal())'}
        : {stata rows(Xref), rows(X), sum(S.wbal())}
        : {stata S.b()}        {it:(obtain coefficients)}
        : {stata S.a()}        {it:(obtain normalizing constant)}
        : {stata S.loss()}     {it:(obtain balancing loss)}
        : {stata end}

{pstd}
    The balancing weights adjust the sample of unionized workers in a
    way such that the means of working hours, work experience, and tenure are
    the same as in the reference sample (non-unionized workers).

{pstd}
    By default, the sum of the balancing weights will be equal to the size
    of the reference sample.

{pstd}
    Balance only a subset of the means and hold the other means fixed at their
    original values:

        . {stata "mata:"}
        : {stata S.adj(2)}      {it:(balance work experience only)}
        : {stata mean(Xref)', mean(X)', mean(X, S.wbal())'}
        : {stata S.noadj(2)}    {it:(do not balance work experience)}
        : {stata mean(Xref)', mean(X)', mean(X, S.wbal())'}
        : {stata end}

{dlgtab:Adjust sample to population}

{pstd}
    From census data you know that the population averages of working hours,
    work experience, and tenure are 35, 10, and 5, respectively. The size of the
    population is 10 million. You want to reweight your sample such that
    it is in line with the population:

        . {stata sysuse nlsw88, clear}
        . {stata drop if missing(hours, ttl_exp, tenure)}
        . {stata "mata:"}
        : {stata X = st_data(., "hours ttl_exp tenure")}
        : {stata mu = (35, 10, 5)}
        : {stata N  = 1e7}
        : {stata S = mm_ebalance()}
        : {stata S.data(X, 1, mu, N)}
        : {stata mean(X)', mean(X,S.wbal())'}
        : {stata S.wsum()}
        : {stata end}


{title:Source code}

{pstd}
    {help moremata11_source##mm_ebalance:mm_ebalance.mata}


{title:References}

{phang}
    Hainmueller, J. (2012). Entropy Balancing for Causal Effects: A
    Multivariate Reweighting Method to Produce Balanced Samples in
    Observational Studies. Political Analysis
    20(1): 25-46. DOI: {browse "http://doi.org/10.1093/pan/mpr025":10.1093/pan/mpr025}
    {p_end}
{phang}
    Hainmueller, J., Y. Xu (2011). EBALANCE: Stata module to perform Entropy
    reweighting to create balanced samples. Statistical Software Components
    S457326, Boston College Department of Economics. {browse "http://ideas.repec.org/c/boc/bocode/s457326.html"}.
    {p_end}
{phang}
    Hainmueller, J., Y. Xu (2013). ebalance: A Stata Package for Entropy Balancing.
    Journal of Statistical Software
    54(7):1-18. DOI: {browse "http://doi.org/10.18637/jss.v054.i07":10.18637/jss.v054.i07}
    {p_end}
{phang}
    Jann, B. (2021a). ebalfit: Stata module to perform entropy balancing. Available
    from http://github.com/benjann/ebalfit/.
    {p_end}
{phang}
    Jann, B. (2021b). Entropy balancing as an estimation command. University of 
    Bern Social Sciences Working Papers 39. Available from
    {browse "http://ideas.repec.org/p/bss/wpaper/39.html"}.
    {p_end}


{title:Author}

{pstd} Ben Jann, University of Bern, ben.jann@unibe.ch


{title:Also see}

{psee}
Online:  help for
{helpb mf_optimize:[M-5] optimize()}, {helpb moremata}, {helpb mf_mm_wbal:mm_wbal()}, {helpb mf_mm_ebal:mm_ebal()},
{helpb ebalfit} (if installed)
{p_end}

